﻿using System;
using System.Collections.Generic;
using Netbits.Common.Collections;
using Netbits.Common.Threading;

namespace Netbits.Common.Logging
{
	/// <summary>
	/// Represents a Log-Group that contains logs
	/// </summary>
	public sealed class LogGroup : IExtended<ListExtended<LogEntry>, LogEntry>, IDisposable
	{
		#region Private Variables

		LockedListExtended<LogEntry> _Logs;
		Lock _Lock;
		LogManager _Manager;
		string _Name;

		bool _IsInfoEnabled;
		bool _IsWarningEnabled;
		bool _IsErrorEnabled;
		bool _IsFatalEnabled;
		bool _IsDebugEnabled;
		bool _Disposed;

		#endregion

		#region Constructor

		internal LogGroup( LogManager Manager_, string Name_ )
		{
			_Name = Name_;
			_Manager = Manager_;
			_Lock = new Lock();
			_Logs = new LockedListExtended<LogEntry>( _Lock );
			_Logs.BeforeRemoveItem.Join( _RemoveLogItem );
			_Logs.MaxItems = 1000;

			_IsInfoEnabled = true;
			_IsWarningEnabled = true;
			_IsErrorEnabled = true;
			_IsFatalEnabled = true;
		}

		#endregion

		#region Public Properties

		/// <summary>
		/// Returns an enumerable list of log-items for this group
		/// </summary>
		public IExtended<ListExtended<LogEntry>, LogEntry> Logs
		{
			get
			{
				return _Logs;
			}
		}

		/// <summary>
		/// Returns the name of this LogGroup
		/// </summary>
		public string Name
		{
			get
			{
				return _Name;
			}
		}

		/// <summary>
		/// Get or set the maximum cached log-items.
		/// -1 for unlimited cache.
		/// default is 1000 items.
		/// </summary>
		public int MaxCache
		{
			get
			{
				return _Logs.MaxItems;
			}
			set
			{
				_Logs.MaxItems = value;
			}
		}

		/// <summary>
		/// Get or sets indicating whether logging is enabled for <c>Info</c> level.
		/// </summary>
		public bool IsInfoEnabled
		{
			get
			{
				try
				{
					_Lock.EnterReadLock();
					return _IsInfoEnabled;
				}
				finally
				{
					_Lock.ExitReadLock();
				}
			}
			set
			{
				try
				{
					_Lock.EnterWriteLock();
					_IsInfoEnabled = value;
				}
				finally
				{
					_Lock.ExitWriteLock();
				}
			}
		}

		/// <summary>
		/// Get or sets indicating whether logging is enabled for <c>Warning</c> level.
		/// </summary>
		public bool IsWarningEnabled
		{
			get
			{
				try
				{
					_Lock.EnterReadLock();
					return _IsWarningEnabled;
				}
				finally
				{
					_Lock.ExitReadLock();
				}
			}
			set
			{
				try
				{
					_Lock.EnterWriteLock();
					_IsWarningEnabled = value;
				}
				finally
				{
					_Lock.ExitWriteLock();
				}
			}
		}

		/// <summary>
		/// Get or sets indicating whether logging is enabled for <c>Error</c> level.
		/// </summary>
		public bool IsErrorEnabled
		{
			get
			{
				try
				{
					_Lock.EnterReadLock();
					return _IsErrorEnabled;
				}
				finally
				{
					_Lock.ExitReadLock();
				}
			}
			set
			{
				try
				{
					_Lock.EnterWriteLock();
					_IsErrorEnabled = value;
				}
				finally
				{
					_Lock.ExitWriteLock();
				}
			}
		}

		/// <summary>
		/// Get or sets indicating whether logging is enabled for <c>Fatal</c> level.
		/// </summary>
		public bool IsFatalEnabled
		{
			get
			{
				try
				{
					_Lock.EnterWriteLock();
					return _IsFatalEnabled;
				}
				finally
				{
					_Lock.ExitReadLock();
				}
			}
			set
			{
				try
				{
					_Lock.EnterWriteLock();
					_IsFatalEnabled = value;
				}
				finally
				{
					_Lock.ExitWriteLock();
				}
			}
		}

		/// <summary>
		/// Get or sets indicating whether logging is enabled for <c>Debug</c> level.
		/// </summary>
		public bool IsDebugEnabled
		{
			get
			{
				try
				{
					_Lock.EnterReadLock();
					return _IsDebugEnabled;
				}
				finally
				{
					_Lock.ExitReadLock();
				}
			}
			set
			{
				try
				{
					_Lock.EnterWriteLock();
					_IsDebugEnabled = value;
				}
				finally
				{
					_Lock.ExitWriteLock();
				}
			}
		}

		#endregion

		#region Public Functions

		/// <summary>
		/// Write the message at the <c>Info</c> level
		/// </summary>
		/// <param name="Message_">Message to write.</param>
		public void Info( string Message_ )
		{
			if( !IsInfoEnabled )
				return;

			_AddLog( LogType.Info, Message_ );
		}

		/// <summary>
		/// Write the message at the <c>Info</c> level with specified formating items
		/// </summary>
		/// <param name="Message_">A string message containing format items.</param>
		/// <param name="Parameters_">Parameters to format</param>
		public void Info( string Message_, params object[] Parameters_ )
		{
			if( !IsInfoEnabled )
				return;

			if( Parameters_ != null && Parameters_.Length > 0 )
				Info( string.Format( Message_, Parameters_ ) );
			else
				Info( Message_ );
		}

		/// <summary>
		/// Write the message at the <c>Info</c> level with specified exception
		/// </summary>
		/// <param name="Message_">An exception to be logged</param>
		public void Info( Exception Message_ )
		{
			if( !IsInfoEnabled )
				return;

			Info( _GetString( Message_ ) );
		}

		/// <summary>
		/// Write the message at the <c>Warning</c> level
		/// </summary>
		/// <param name="Message_">Message to write</param>
		public void Warning( string Message_ )
		{
			if( !IsWarningEnabled )
				return;

			_AddLog( LogType.Warning, Message_ );
		}

		/// <summary>
		/// Write the message at the <c>Warning</c> level with specified formating items
		/// </summary>
		/// <param name="Message_">A string message containing format items</param>
		/// <param name="Parameters_">Parameters to format</param>
		public void Warning( string Message_, params object[] Parameters_ )
		{
			if( !IsWarningEnabled )
				return;

			if( Parameters_ != null && Parameters_.Length > 0 )
				Warning( string.Format( Message_, Parameters_ ) );
			else
				Warning( Message_ );
		}

		/// <summary>
		/// Write the message at the <c>Warning</c> level with specified exception
		/// </summary>
		/// <param name="Message_">An exception to be logged</param>
		public void Warning( Exception Message_ )
		{
			if( !IsWarningEnabled )
				return;

			Warning( _GetString( Message_ ) );
		}

		/// <summary>
		/// Write the message at the <c>Error</c> level
		/// </summary>
		/// <param name="Message_">Message to write</param>
		public void Error( string Message_ )
		{
			if( !IsErrorEnabled )
				return;

			_AddLog( LogType.Error, Message_ );
		}

		/// <summary>
		/// Write the message at the <c>Error</c> level with specified formating items
		/// </summary>
		/// <param name="Message_">A string message containing format items</param>
		/// <param name="Parameters_">Parameters to format</param>
		public void Error( string Message_, params object[] Parameters_ )
		{
			if( !IsErrorEnabled )
				return;

			if( Parameters_ != null && Parameters_.Length > 0 )
				Error( string.Format( Message_, Parameters_ ) );
			else
				Error( Message_ );
		}

		/// <summary>
		/// Write the message at the <c>Error</c> level with specified exception
		/// </summary>
		/// <param name="Message_">An exception to be logged</param>
		public void Error( Exception Message_ )
		{
			if( !IsErrorEnabled )
				return;

			Error( _GetString( Message_ ) );
		}

		/// <summary>
		/// Write the message at the <c>Fatal</c> level
		/// </summary>
		/// <param name="Message_">Message to write</param>
		public void Fatal( string Message_ )
		{
			if( !IsFatalEnabled )
				return;

			_AddLog( LogType.Fatal, Message_ );
		}

		/// <summary>
		/// Write the message at the <c>Fatal</c> level with specified formating items
		/// </summary>
		/// <param name="Message_">A string message containing format items</param>
		/// <param name="Parameters_">Parameters to format</param>
		public void Fatal( string Message_, params object[] Parameters_ )
		{
			if( !IsFatalEnabled )
				return;

			if( Parameters_ != null && Parameters_.Length > 0 )
				Fatal( string.Format( Message_, Parameters_ ) );
			else
				Fatal( Message_ );
		}

		/// <summary>
		/// Write the message at the <c>Fatal</c> level with specified exception
		/// </summary>
		/// <param name="Message_">An exception to be logged</param>
		public void Fatal( Exception Message_ )
		{
			if( !IsFatalEnabled )
				return;

			Fatal( _GetString( Message_ ) );
		}

		/// <summary>
		/// Write the message at the <c>Debug</c> level
		/// </summary>
		/// <param name="Message_">Message to write</param>
		public void Debug( string Message_ )
		{
			if( !IsDebugEnabled )
				return;

			_AddLog( LogType.Debug, Message_ );
		}

		/// <summary>
		/// Write the message at the <c>Debug</c> level with specified formating items
		/// </summary>
		/// <param name="Message_">A string message containing format items</param>
		/// <param name="Parameters_">Parameters to format</param>
		public void Debug( string Message_, params object[] Parameters_ )
		{
			if( !IsDebugEnabled )
				return;

			if( Parameters_ != null && Parameters_.Length > 0 )
				Debug( string.Format( Message_, Parameters_ ) );
			else
				Debug( Message_ );
		}

		/// <summary>
		/// Write the message at the <c>Debug</c> level with specified exception
		/// </summary>
		/// <param name="Message_">An exception to be logged</param>
		public void Debug( Exception Message_ )
		{
			if( !IsDebugEnabled )
				return;

			Debug( _GetString( Message_ ) );
		}

		#endregion

		#region Private Functions

		string _GetString( Exception Exception_ )
		{
#if DEBUG
			return string.Format( "{0}{1}{2}{1}{3}{1}", Exception_.Message, Environment.NewLine, Exception_.StackTrace, Exception_.Source );
#else
			return Exception_.Message;
#endif
		}

		void _AddLog( LogType Type_, string Message_ )
		{
			LogEntry le = new LogEntry( this, DateTime.Now.Ticks, Type_, Message_ );

			if( !_Manager.IsCachingDisabled )
				_Logs.Add( le );

			_Manager.__Add( le );
		}

		void _RemoveLogItem( ListExtended<LogEntry> Sender_, LogEntry Item_ )
		{
			_Manager.__Remove( Item_ );
		}

		#endregion

		#region IExtended

		/// <summary>
		/// Creates a copy of all log items in the list in a new List
		/// </summary>
		/// <returns>The new list</returns>
		public List<LogEntry> CopyToNewList()
		{
			return _Logs.CopyToNewList();
		}

		/// <summary>
		/// Get or sets the maximum items that the list can contain.
		/// -1 for unlimited items.
		/// </summary>
		public int MaxItems
		{
			get
			{
				return _Logs.MaxItems;
			}
			set
			{
				_Logs.MaxItems = value;
			}
		}

		/// <summary>
		/// Get the last log-item of the list
		/// </summary>
		public LogEntry First
		{
			get
			{
				return _Logs.First;
			}
		}

		/// <summary>
		/// Get the first log-item of the list. 
		/// </summary>
		public LogEntry Last
		{
			get
			{
				return _Logs.Last;
			}
		}

		/// <summary>
		/// Get the enumerator that iterates through the list
		/// </summary>
		/// <returns>The new enumerator</returns>
		public System.Collections.Generic.IEnumerator<LogEntry> GetEnumerator()
		{
			return _Logs.GetEnumerator();
		}

		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return _Logs.GetEnumerator();
		}

		/// <summary>
		/// Get or set the item at the specified index-position
		/// </summary>
		/// <param name="Index_">The index of the item to get or set</param>
		/// <returns>The item at the specified index-position, otherwise IndexOutOfRangeException</returns>
		public LogEntry this[int Index_]
		{
			get
			{
				return _Logs[Index_];
			}
		}

		/// <summary>
		/// Get the number of items actually contained in the list.
		/// </summary>
		public int Count
		{
			get
			{
				return _Logs.Count;
			}
		}

		/// <summary>
		/// Occurs before a log-item is added to list
		/// </summary>
		public SaveEventHandler<ListExtended<LogEntry>, LogEntry> BeforeAddItem
		{
			get
			{
				return _Logs.BeforeAddItem;
			}
		}

		/// <summary>
		/// Occurs after a log-item was added to list
		/// </summary>
		public SaveEventHandler<ListExtended<LogEntry>, LogEntry> AfterAddItem
		{
			get
			{
				return _Logs.AfterAddItem;
			}
		}

		/// <summary>
		/// Occurs before a log-item is insert on specified index-position.
		/// </summary>
		public SaveEventHandler<ListExtended<LogEntry>, int, LogEntry> BeforeInsertItem
		{
			get
			{
				return _Logs.BeforeInsertItem;
			}
		}

		/// <summary>
		/// Occurs after a log-item was insert at specified index-position
		/// </summary>
		public SaveEventHandler<ListExtended<LogEntry>, int, LogEntry> AfterInsertItem
		{
			get
			{
				return _Logs.AfterInsertItem;
			}
		}

		/// <summary>
		/// Occurs before a log-item was removed from the list
		/// </summary>
		public SaveEventHandler<ListExtended<LogEntry>, LogEntry> BeforeRemoveItem
		{
			get
			{
				return _Logs.BeforeRemoveItem;
			}
		}

		/// <summary>
		/// Occurs after a log-item was removed from list.
		/// </summary>
		public SaveEventHandler<ListExtended<LogEntry>, LogEntry> AfterRemoveItem
		{
			get
			{
				return _Logs.AfterRemoveItem;
			}
		}

		/// <summary>
		/// Occurs before a log-item was removed from the list at specified index.
		/// </summary>
		public SaveEventHandler<ListExtended<LogEntry>, int, LogEntry> BeforeRemoveItemAt
		{
			get
			{
				return _Logs.BeforeRemoveItemAt;
			}
		}

		/// <summary>
		/// Occurs after a log-item was successfully removed from the list at the specified index
		/// </summary>
		public SaveEventHandler<ListExtended<LogEntry>, int, LogEntry> AfterRemoveItemAt
		{
			get
			{
				return _Logs.AfterRemoveItemAt;
			}
		}

		/// <summary>
		/// Occurs before a log-item was modified 
		/// </summary>
		public SaveEventHandler<ListExtended<LogEntry>, LogEntry> BeforeModifyItem
		{
			get
			{
				return _Logs.BeforeModifyItem;
			}
		}

		/// <summary>
		/// Occurs after a log-item was modified
		/// </summary>
		public SaveEventHandler<ListExtended<LogEntry>, LogEntry> AfterModifyItem
		{
			get
			{
				return _Logs.AfterModifyItem;
			}
		}

		/// <summary>
		/// Occurs before the log-list was cleared
		/// </summary>
		public SaveEventHandler<ListExtended<LogEntry>> BeforeClearItems
		{
			get
			{
				return _Logs.BeforeClearItems;
			}
		}

		/// <summary>
		/// Occurs after the log-list is cleared
		/// </summary>
		public SaveEventHandler<ListExtended<LogEntry>> AfterClearItems
		{
			get
			{
				return _Logs.AfterClearItems;
			}
		}

		/// <summary>
		/// Get or set if the log-list use asynchrony events.
		/// </summary>
		public bool UseAsyncEvents
		{
			get
			{
				return _Logs.UseAsyncEvents;
			}
			set
			{
				_Logs.UseAsyncEvents = value;
			}
		}

		/// <summary>
		/// After this function is called the calling thread was blocked until any log-item is in the list
		/// </summary>
		public void WaitForItems()
		{
			_Logs.WaitForItems();
		}

		/// <summary>
		/// After this function is called the calling thread was blocked until any log-item is in the list or the specified timeout was reached
		/// </summary>
		/// <param name="Timeout_">The specified timeout to wait</param>
		public bool WaitForItems( int Timeout_ )
		{
			return _Logs.WaitForItems( Timeout_ );
		}

		/// <summary>
		/// Get the Wait-Handler (ManualResetEventSlim)
		/// </summary>
		/// <returns>The Wait-Handler</returns>
		public System.Threading.ManualResetEventSlim WaitHandler()
		{
			return _Logs.WaitHandler();
		}

		#endregion

		#region IDisposable

		void Dispose( bool Disposing_ )
		{
			if( !_Disposed )
			{
				if( Disposing_ )
				{
					if( _Logs != null )
						_Logs.Clear();

					if( _Lock != null )
						_Lock.Dispose();

					if( _Logs != null )
						_Logs.Dispose();

					_Manager = null;
					_Name = string.Empty;
				}
				_Disposed = true;
			}			
		}

		/// <summary>
		/// Dispose LogGroup Resources
		/// </summary>
		public void Dispose()
		{
			Dispose( true );
			GC.SuppressFinalize( this );
		}

		#endregion
	}
}
